package com.ruoyi.dj.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.dj.domain.*;
import com.ruoyi.dj.service.IMsModelCalcService;
import com.ruoyi.modelbuild.service.ModelAdjustService;
import com.ruoyi.system.domain.ResultSave;
import com.ruoyi.system.service.IResultSaveService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 融合数据
 */
@Slf4j
@Service
public class MsModelCalcServiceImpl implements IMsModelCalcService {

    @Autowired
    ModelAdjustService adjustService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IResultSaveService resultSaveService;
    /**
     * 垂直分布模拟 处理
     * @param param
     * @return
     */
    @Override
    public ModelCalcGlobalVO globalCal(ModelCalcGlobalParam param) {
        ModelCalcGlobalVO modelCalcGlobalVO = globalPreCal(param);
        //页面需将 json文件转为 json字符串
        List<String> dataPaths = modelCalcGlobalVO.getDataPaths();
        List<JSONObject> dataList = new ArrayList<>(dataPaths.size());
        for (String dataPath : dataPaths) {
            String s = FileUtil.readString(dataPath, Charset.forName("utf-8"));
            JSONObject jsonObject = JSONUtil.parseObj(s);
            dataList.add(jsonObject);
        }
        modelCalcGlobalVO.setDatas(dataList);
        modelCalcGlobalVO.setDataPaths(null);
        return modelCalcGlobalVO;
    }

    //从产品表查询系数详情
    private String findIndex(String indexId ) {
        ResultSave recordPo = resultSaveService.getById(indexId);
        String resultData = recordPo.getResultData();
        String indexJson = JSONUtil.parseArray(resultData).getStr(0);
        return indexJson;
    }


    public ModelCalcGlobalVO globalPreCal(ModelCalcGlobalParam param) {

        String key = MD5.create().digestHex(param.toString());
        Object cache = redisTemplate.opsForValue().get(key);
        if (cache != null) {
            ModelCalcGlobalVO modelCalcGlobalVO = dealWithResJSON((String) cache);
            return modelCalcGlobalVO;
        }

        //1 找到最匹配的系数文件
        String jsonFile = findIndex(param.getIndex());


        //2组装参数
        List<String> commonds = new ArrayList<>();
        commonds.add("python");
        String script = PathUtil.codeModuleDir(BizModule.MODEL_HIGH_PRECISION) + PathUtil.moduleDirs().getHighPrecision().get("global");
        commonds.add(script);

        //时间
        String start = DateUtil.format(param.getDenStartTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        String end = DateUtil.format(param.getDenEndTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        commonds.add("--datetime="+start+","+end+","+param.getTimeStep());

        ////找到温度修正系数
        commonds.add("-fp");
        commonds.add(jsonFile);
        commonds.add("--lon="+param.getMinLon()+","+param.getMaxLon()+","+param.getLonStep());
        commonds.add("--lat="+param.getMinLat()+","+param.getMaxLat()+","+param.getLatStep());
        commonds.add("--alt="+param.getAlt());

        //预报方法
        //如果 页面关闭,则传 -envi=obs，开 =forecast
        if (StringUtils.isNotEmpty(param.getSunMethod()) && StringUtils.isNotEmpty(param.getEarthMethod())) {
            commonds.add("-envi");
            commonds.add("forecast");
            commonds.add("-epf");
            commonds.add(param.getSunMethod()+","+param.getEarthMethod());
        }else {
            commonds.add("-envi");
            commonds.add("obs");
        }


        String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        String path = PathUtil.outputModuleDir(BizModule.MODEL_HIGH_PRECISION) + "/global/"+yyyyMMdd+".log";
        try {
            new File(path).createNewFile();
        } catch (IOException e) {
        }
        commonds.add("-lp");
        commonds.add(path);

        commonds.add("-op");
        commonds.add("/dgdq/tmp/ModelCal/ModelBuild");


        String execCommond = CollectionUtil.join(commonds, " ");
        log.info("执行命令:{}",execCommond);
        String res = RuntimeUtil.execForStr(execCommond);
        log.info("执行结果:{}",res);

        ModelCalcGlobalVO resVO = dealWithResJSON(res);
        redisTemplate.opsForValue().set(key,res,1, TimeUnit.HOURS);
        return resVO;
    }


    /**
     * 讲结果 的 图片和 数据
     * @param res
     * @return
     */
    private ModelCalcGlobalVO dealWithResJSON(String res) {
        String[] split = res.split("###");
        res = split[1];
        //{'2004040900': '/dgdq/tmp/ModelCal/ModelBuild/png/horizon_dens_2004040900_100.0km_-180.0_-10.0_180.0_-10.0.png',
        // '2004040912': '/dgdq/tmp/ModelCal/ModelBuild/png/horizon_dens_2004040912_100.0km_-180.0_-10.0_180.0_-10.0.png',
        // '2004041000': '/dgdq/tmp/ModelCal/ModelBuild/png/horizon_dens_2004041000_100.0km_-180.0_-10.0_180.0_-10.0.png'},
        // {'2004040900': '/dgdq/tmp/ModelCal/ModelBuild/data/horizon_jsonfile_2004040900_100.0km_-180.0_-10.0_180.0_-10.0.json',
        // '2004040912': '/dgdq/tmp/ModelCal/ModelBuild/data/horizon_jsonfile_2004040912_100.0km_-180.0_-10.0_180.0_-10.0.json',
        // '2004041000': '/dgdq/tmp/ModelCal/ModelBuild/data/horizon_jsonfile_2004041000_100.0km_-180.0_-10.0_180.0_-10.0.json'}
        String[] strs = res.replace("},{", "}~{").split("~");
        JSONObject timeObj = JSONUtil.parseObj(strs[0]);
        JSONObject dataObj = JSONUtil.parseObj(strs[1]);

        ModelCalcGlobalVO resVO = new ModelCalcGlobalVO();
        List<String> pngList = new ArrayList<>();
        for (Map.Entry<String, Object> stringObjectEntry : timeObj) {
            pngList.add((String)stringObjectEntry.getValue());
        }
        resVO.setPngs(pngList);
        List<String> dataList = new ArrayList<>();
        for (Map.Entry<String, Object> stringObjectEntry : dataObj) {
            String value = (String) stringObjectEntry.getValue();
            dataList.add(value);
        }
        resVO.setDataPaths(dataList);
        return resVO;
    }


    @Override
    public String globalDownload(ModelCalcGlobalParam param) {
        ModelCalcGlobalVO vo = globalPreCal(param);
        List<String> pngs = vo.getPngs();
        List<String> datas = vo.getDataPaths();
        List<String> merge = new ArrayList<>();
        merge.addAll(pngs);
        merge.addAll(datas);
        List<File> collect = merge.stream().map(a -> new File(a)).collect(Collectors.toList());
        File[] files = ArrayUtil.toArray(collect, File.class);

        String url = PathUtil.tmpModuleDir(BizModule.MODEL_CAL)+ "/"+ DateUtil.format(new Date(),"yyyyMMddHHmmss")+".zip";
        File zip = ZipUtil.zip(new File(url), false, files);
        return url;
    }

    @Override
    public TrackCalVO trackCal(ModelCalcTrackParam param) throws Exception {

        String key = MD5.create().digestHex(param.toString());
        Object cache = redisTemplate.opsForValue().get(key);
        String csvPath = null;
        if (cache != null) {
            csvPath = (String) cache;
        }else {

            //1 找到最匹配的系数文件
            String jsonFile = findIndex(param.getIndex());

            //2组装参数
            List<String> commonds = new ArrayList<>();
            commonds.add("python");
            String script = PathUtil.codeModuleDir(BizModule.MODEL_HIGH_PRECISION) + PathUtil.moduleDirs().getHighPrecision().get("track");
            commonds.add(script);

            commonds.add("-sn");
            commonds.add(param.getDenSate());

            //时间
            //String start = DateUtil.format(param.getDenStartTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            //String end = DateUtil.format(param.getDenEndTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            //commonds.add("--datetime=" + start + "," + end + "," + param.getStep());

            //系数
            commonds.add("-fp");
            commonds.add(jsonFile);


            commonds.add("-op");
            commonds.add("/dgdq/tmp/ModelCal/ModelBuild");
            //

            //预报方法
            if (StringUtils.isNotEmpty(param.getSunMethod()) && StringUtils.isNotEmpty(param.getEarthMethod())) {
                commonds.add("-envi");
                commonds.add("forecast");
                commonds.add("-epf");
                commonds.add(param.getSunMethod()+","+param.getEarthMethod());
            }else {
                commonds.add("-envi");
                commonds.add("obs");
            }

            String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            String path = PathUtil.outputModuleDir(BizModule.MODEL_HIGH_PRECISION) + "/track/" + yyyyMMdd+".log";
            new File(path).createNewFile();
            //日志
            commonds.add("-lp");
            commonds.add(path);


            String execCommond = CollectionUtil.join(commonds, " ");
            log.info("执行命令:{}", execCommond);
            String res = RuntimeUtil.execForStr(execCommond);
            log.info("执行结果:{}", res);
            //###/dgdq/tmp/ModelCal/ModelBuild/data/orbit_dens_54AR_LSTM_20050403000000_20050404120000.csv###
            String[] split = res.split("###");
            csvPath = split[1];
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(csvPath), 1, TimeUnit.HOURS);
        }

        if (null != csvPath) {
            //读取文件
            BufferedReader reader = new BufferedReader(new FileReader(csvPath));
            //忽略首行
            reader.readLine();
            String line = "";
            List nums = new ArrayList<>(110);
            List times = new ArrayList<>(110);

            long lines = Files.lines(Paths.get(csvPath)).count();
            //页面展示1000条数据
            int num = 1000;
            int skip = 1;
            if (lines > 1000) {
                skip = (int) (lines / num);
            }
            int lineNum = 0;
            while ((line = reader.readLine()) != null) {
                if (lineNum % skip == 0) {
                    String[] split = line.split(",");
                    nums.add(split[1]);
                    times.add(split[0]);
                }
                lineNum++;
            }
            TrackCalVO trackCalVO = new TrackCalVO();
            trackCalVO.setTime(times);
            trackCalVO.setModeled(nums);
            return trackCalVO;
        }else {
            return null;
        }

    }

    @Override
    public String trackDownload(ModelCalcTrackParam param) throws Exception {
        String key = MD5.create().digestHex(param.toString());
        Object cach = redisTemplate.opsForValue().get(key);
        if (cach == null) {
            trackCal(param);
            cach = redisTemplate.opsForValue().get(key);
        }
        String cvsPath = (String) cach;
        String url = PathUtil.tmpModuleDir(BizModule.MODEL_CAL)+ "/"+ DateUtil.format(new Date(),"yyyyMMddHHmmss")+".zip";
        File zip = ZipUtil.zip(new File(url), false, new File(cvsPath));
        return url;
    }

    @Override
    public String verticalDownload(ModelCalcVerticalParam param) {
        ModelCalcGlobalVO vo = verticalPreCal(param);
        List<String> pngs = vo.getPngs();
        List<String> datas = vo.getDataPaths();
        List<String> merge = new ArrayList<>();
        merge.addAll(pngs);
        merge.addAll(datas);
        List<File> collect = merge.stream().map(a -> new File(a)).collect(Collectors.toList());
        File[] files = ArrayUtil.toArray(collect, File.class);

        String url = PathUtil.tmpModuleDir(BizModule.MODEL_CAL)+ "/"+ DateUtil.format(new Date(),"yyyyMMddHHmmss")+".zip";
        File zip = ZipUtil.zip(new File(url), false, files);
        return url;
    }

    /**
     * 垂直分布模拟 处理
     * @param param
     * @return
     */
    @Override
    public ModelCalcGlobalVO verticalCal(ModelCalcVerticalParam param) {
        ModelCalcGlobalVO modelCalcGlobalVO = verticalPreCal(param);
        //页面需将 json文件转为 json字符串
        List<String> dataPaths = modelCalcGlobalVO.getDataPaths();
        List<JSONObject> dataList = new ArrayList<>(dataPaths.size());
        for (String dataPath : dataPaths) {
            String s = FileUtil.readString(dataPath, Charset.forName("utf-8"));
            JSONObject jsonObject = JSONUtil.parseObj(s);
            dataList.add(jsonObject);
        }
        modelCalcGlobalVO.setDatas(dataList);
        modelCalcGlobalVO.setDataPaths(null);
        return modelCalcGlobalVO;
    }


    private ModelCalcGlobalVO verticalPreCal(ModelCalcVerticalParam param) {
        String key = MD5.create().digestHex(param.toString());
        Object cache = redisTemplate.opsForValue().get(key);
        if (cache != null) {
            ModelCalcGlobalVO modelCalcGlobalVO = dealWithResJSON((String) cache);
            return modelCalcGlobalVO;
        }


        //1组装参数
        List<String> commonds = new ArrayList<>();
        commonds.add("python");
        String script = PathUtil.codeModuleDir(BizModule.MODEL_HIGH_PRECISION) + PathUtil.moduleDirs().getHighPrecision().get("vertical");
        commonds.add(script);


        //时间
        String start = DateUtil.format(param.getStartDate(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        String end = DateUtil.format(param.getEndDate(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        commonds.add("--datetime="+start+","+end+","+param.getTimeStep());

        //找到温度修正系数
        commonds.add("-fp");
        //1 找到最匹配的系数文件
        String jsonFile = findIndex(param.getIndex());
        commonds.add(jsonFile);

        if("jx".equals(param.getType())) {
            //径向 经度只有一个值
            commonds.add("--lon=" + param.getLon() + "," + param.getLon() + ",1");
            commonds.add("--lat=" + param.getLatMin() + "," + param.getLatMax() + "," + param.getStep());
        }else if ("wx".equals(param.getType())) {
            //纬向 纬度只有一个值
            commonds.add("--lon=" + param.getLonMin() + "," + param.getLonMax() + "," + param.getStep());
            commonds.add("--lat=" + param.getLat() + "," + param.getLat() + ",1");
        }


        commonds.add("--alt=" + param.getAltMin() + "," + param.getAltMax() + "," + param.getAltStep());

        //预报方法
        if (StringUtils.isNotEmpty(param.getSunMethod()) && StringUtils.isNotEmpty(param.getEarthMethod())) {
            commonds.add("-envi");
            commonds.add("forecast");
            commonds.add("-epf");
            commonds.add(param.getSunMethod()+","+param.getEarthMethod());
        }else {
            commonds.add("-envi");
            commonds.add("obs");
        }


        String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        String path = PathUtil.outputModuleDir(BizModule.MODEL_HIGH_PRECISION) + "/vertical/"+yyyyMMdd+".log";
        try {
            new File(path).createNewFile();
        } catch (IOException e) {
        }
        commonds.add("-lp");
        commonds.add(path);
        commonds.add("-op");
        commonds.add("/dgdq/tmp/ModelCal/ModelBuild");

        String execCommond = CollectionUtil.join(commonds, " ");
        log.info("执行命令:{}",execCommond);
        String res = RuntimeUtil.execForStr(execCommond);
        log.info("执行结果:{}",res);
        JSONObject resObj = JSONUtil.parseObj(res);
        if (resObj.getInt("code")==1) {
            //{'png_path': {'2005010900': '/dgdq/tmp/modelBuild/png/modeling_horizon_density_2005010900_480.0km_-180.0_90.0_179.0_-90.0.png', '2005011000': '/dgdq/tmp/modelBuild/png/modeling_horizon_density_2005011000_480.0km_-180.0_90.0_179.0_-90.0.png'},
            // 'code': 1, 'msg': 'Success'}
            ModelCalcGlobalVO resVO = dealWithResJSON(res);
            redisTemplate.opsForValue().set(key,res,1, TimeUnit.HOURS);
            return resVO;

        }else {
            log.error("执行结果:{}",res);
            throw new ServiceException("脚本执行异常:"+resObj.getStr("msg"));
        }
    }
}
